﻿using System;

namespace SRS.Encryption
{
    /// <summary>
    /// Cipher Feedback
    /// </summary>
    public class CFB : SRSEncryption
    {
        public CFB()
            : base()
        {
        }

        #region Encrypt
        /// <summary>
        /// Encrypt. This function will call Encrypt with unitLength equal to 1.
        /// </summary>
        /// <param name="key">Encryption key</param>
        /// <param name="plainText">Plain text which are encrypted</param>
        /// <returns></returns>
        public override byte[] Encrypt(byte[] key, byte[] plainText)
        {
            return Encrypt(key, plainText, 1);
        }


        /// <summary>
        /// Encrypt
        /// </summary>
        /// <param name="key">Encryption key</param>
        /// <param name="plainText">Plain text which are encrypted</param>
        /// <param name="unitLength"></param>
        /// <returns></returns>
        public override byte[] Encrypt(byte[] key, byte[] plainText, int unitLength)
        {
            int i;
            int length = plainText.Length;
            int keyLength = key.Length;
            byte[] cipherText = new byte[GenerateNewLength(length, unitLength)];
            byte[] cipherTextBlock = null;
            byte[] plainTextBlock = null;
            byte[] xi = GenerateInitialVector(key);

            for (i = 0; i < length; i += unitLength)
            {
                plainTextBlock = GetBlock(plainText, i, unitLength);
                cipherTextBlock = XORBlock(plainTextBlock, MSB(EncryptBlock(key, xi), unitLength));
                if (i + unitLength < length)
                {
                    xi = Concat(LSB(xi, keyLength - unitLength), cipherTextBlock);
                }

                Array.Copy(cipherTextBlock, 0, cipherText, i, unitLength);
            }

            return cipherText;
        }
        #endregion


        #region Decrypt
        /// <summary>
        /// Decrypt. This function will call Decrypt with unitLength equal to 1.
        /// </summary>
        /// <param name="key">Decryption key</param>
        /// <param name="cipherText">Cipher text which are decrypted</param>
        /// <returns></returns>
        public override byte[] Decrypt(byte[] key, byte[] cipherText)
        {
            return Decrypt(key, cipherText, 1);
        }


        /// <summary>
        /// Decrypt
        /// </summary>
        /// <param name="key">Decryption key</param>
        /// <param name="cipherText">Cipher text which are decrypted</param>
        /// <param name="unitLength"></param>
        /// <returns></returns>
        public override byte[] Decrypt(byte[] key, byte[] cipherText, int unitLength)
        {
            int i;
            int length = cipherText.Length;
            int keyLength = key.Length;
            byte[] plainText = new byte[GenerateNewLength(length, unitLength)];
            byte[] plainTextBlock = null;
            byte[] cipherTextBlock = null;
            byte[] xi = GenerateInitialVector(key);

            for (i = 0; i < length; i += unitLength)
            {
                cipherTextBlock = GetBlock(cipherText, i, unitLength);
                plainTextBlock = XORBlock(cipherTextBlock, MSB(EncryptBlock(key, xi), unitLength));
                if (i + unitLength < length)
                {
                    xi = Concat(LSB(xi, keyLength - unitLength), cipherTextBlock);
                }

                Array.Copy(plainTextBlock, 0, plainText, i, unitLength);
            }

            return plainText;
        }
        #endregion
    }
}
